package expbase

import (
	"context"
	"errors"
	"gitee.com/wudicaidou/menciis-pocx/pocbase"
	shttp "gitee.com/wudicaidou/menciis-shttp"
	"strconv"
)

type ExpType string

const (
	// 插件类型，也可以理解为插件能力
	// 文件上传不作为一种利用类型，文件上传最终应该可以 shell
	// TypeArbitraryFileUpload     = "arbitrary_file_upload"

	TypeCommandExecution      = "command_execution"
	TypeJavaCodeExecution     = "java_code_execution"
	TypePHPCodeExecution      = "php_code_execution"
	TypeNodeCodeExecution     = "node_code_execution"
	TypeArbitraryFileDownload = "arbitrary_file_download"
	TypeSensitiveInfoLeak     = "sensitive_info_leak"
)

var (
	ErrUnsupportedMethod = errors.New("unsupported method")
	ErrEmptyReqAsset     = errors.New("empty request asset")
)

type ExpMeta struct {
	Id      string
	Name    string
	VulId   string
	Ability ExpType // 插件最大的能力
	Echo    bool    // 是否有执行回显
}

// ExpContext 是 drops 执行过程的上下文，这里面包含了一些配置信息和输入输出的接口
type ExpContext struct {
	Target     pocbase.Asset
	HttpClient *shttp.Client
	Writer     pocbase.EventWriter

	// []string{ "/etc/passwd", "/root/.ssh/authorized_keys" }
	DownloadFileList []string
	// []string{ "whoami", "curl http://xxxx" }
	CommandToExecute []string
	// PHPCodeToExecute
	// JavaCodeToExecute
}

// ExploitPlugin 是所有的 drops 类的插件需要实现的接口
type ExploitPlugin interface {
	Meta() *ExpMeta
	Init(ctx context.Context) error
	GetBasicInfo(ctx context.Context, expContext *ExpContext) ([]*BasicInfoEvent, error)
	DownloadFile(ctx context.Context, expContext *ExpContext) ([]*FileDownloadEvent, error)
	ExecuteCommand(ctx context.Context, expContext *ExpContext) ([]*CommandResultEvent, error)
	Close()
}

type BaseExploitPlugin struct {
}

func (b *BaseExploitPlugin) Init(ctx context.Context) error {
	return nil
}

func (b *BaseExploitPlugin) Close() {}

func (b *BaseExploitPlugin) GetEventBase(target pocbase.Asset, plugin ExploitPlugin) EventBase {
	var targetInfo pocbase.TargetInfo
	reqAsset, ok := target.(*pocbase.RequestAsset)
	if ok {
		req := reqAsset.Req
		targetInfo.Host = req.GetHost()
		p, err := strconv.Atoi(req.GetPort())
		if err != nil {
			if req.GetUrl().Scheme == "http" {
				p = 80
			} else {
				p = 443
			}
		}
		targetInfo.Port = p
		targetInfo.Path = req.GetUrl().Path
	} else {
		if serviceAsset, ok := target.(*pocbase.ServiceAsset); ok {
			targetInfo.Host = serviceAsset.Host
			targetInfo.Port = serviceAsset.Port
		}
	}
	return EventBase{
		Target: &targetInfo,
		Plugin: &pocbase.PluginInfo{
			ID: plugin.Meta().Id,
		},
	}
}
